package com.yy.ttmodule.activity;


import android.annotation.SuppressLint;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

import com.yy.ttmodule.control.ParamCons;
import com.yy.ttmodule.udp.ConvertData;
import com.yy.ttmodule.udp.DataTypeConversion;
import com.yy.ttmodule.udp.UdpCommand;
import com.yy.ttmodule.udp.UdpDataUtil;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Arrays;

public class UdpClient {

    private static String TAG = "UdpClient";
    private static UdpClient mSocketClient = null;

    private DatagramSocket mSocket;
    private DatagramPacket sendPacket;    //发送
    private DatagramPacket receivePacket; //接受

    private SocketThread mSocketThread;
    private boolean isStop = false;//thread flag

    private String serverIp;
    private int serverPort;
    private int clientPort;

    public static UdpClient getInstance() {
        if(mSocketClient == null){
            synchronized (UdpClient.class) {
                mSocketClient = new UdpClient();
            }
        }
        return mSocketClient;
    }

    private class SocketThread extends Thread {

        private String serverIp;
        private int serverPort;
        private int clientPort;

        public SocketThread(String ip, int serverPort, int clientPort){
            this.serverIp = ip;
            this.serverPort = serverPort;
            this.clientPort = clientPort;
        }

        @Override
        public void run() {
            Log.d(TAG,"SocketThread start ");
            super.run();

            //connect ...
            try {
                if (mSocket != null) {
                    mSocket.close();
                    mSocket = null;
                }

                InetAddress ipAddress = InetAddress.getByName(serverIp);
                mSocket = new DatagramSocket(clientPort);
                mSocket.connect(ipAddress, serverPort); //连接

                //设置timeout
                //mSocket.setSoTimeout(3000);
                Log.d(TAG,"udp connect = " + isConnect());

                if(isConnect()) {
                    isStop = false;
                    //uiHandler.sendEmptyMessage(1);
                } else{
                    //uiHandler.sendEmptyMessage(-1);
                    Log.e(TAG, "SocketThread connect fail");
                    //return;
                }

            } catch (IOException e) {
                //uiHandler.sendEmptyMessage(-1);
                Log.e(TAG,"SocketThread connect io exception = "+e.getMessage());
                e.printStackTrace();
                //return;
            }
            Log.d(TAG,"SocketThread connect over ");

            //发送一次，否则不发送则收不到，不知道为啥。。。
            //sendByteCmd(new byte[]{00},-1);//send once

            //read ...
            while (isConnect() && !isStop && !isInterrupted()) {
                int size;
                Log.d(TAG,"SocketThread DatagramPacket");
                try {
                    byte[] preBuffer = new byte[4 * 1024];//预存buffer
                    receivePacket = new DatagramPacket(preBuffer, preBuffer.length);
                    mSocket.receive(receivePacket);

                    if (receivePacket.getData() != null) {
                        size = receivePacket.getLength();     //此为获取后的有效长度，一次最多读64k，预存小的话可能分包
                        Log.d(TAG, "receivePacket = "
                                + ConvertData.byteArrayToHexString(receivePacket.getData(), size));
                        if (size > 0) {
                            Message msg = new Message();
                            msg.what = MSG_REV_DATA;
                            Bundle bundle = new Bundle();
                            bundle.putByteArray("data", receivePacket.getData());
                            bundle.putInt("size", size);
//                            bundle.putInt("requestCode", requestCode);
                            msg.setData(bundle);
                            uiHandler.sendMessage(msg);
                        }
                    }
                    Thread.sleep(100);
                } catch (Exception e) {
                    Log.e(TAG,"SocketThread read io exception = " + e.getMessage());
                    e.printStackTrace();
                }
            }
        }
    }

    public void connect(String ip, int serverport, int clientport) {
        this.serverIp = ip;
        this.serverPort = serverport;
        this.clientPort = clientport;
        mSocketThread = new SocketThread(ip, serverport, clientport);
        mSocketThread.start();
    }

    public boolean isConnect() {
        boolean flag = false;
        if (mSocket != null) {
            flag = mSocket.isConnected();
        }
        return flag;
    }

    public void disconnect() {
        isStop = true;
        if (mSocket != null) {
            mSocket.close();
            mSocket = null;
        }
        if (mSocketThread != null) {
            mSocketThread.interrupt();//not intime destory thread,so need a flag
        }
    }

    public void sendByteCmd(final byte[] mBuffer) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    InetAddress ipAddress = InetAddress.getByName(serverIp);
                    sendPacket = new DatagramPacket(mBuffer, mBuffer.length, ipAddress, serverPort);
                    mSocket.send(sendPacket);
                    Log.d(TAG,"sendByteCmd DatagramPacket");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    public void sendData(byte[] parambytes, byte[] databytes) {
        byte[] srcBytes = new byte[UdpCommand.RECEIVE_PACK_LEN_MAX];
        byte[] resBytes = new byte[UdpCommand.RECEIVE_PACK_LEN_MAX];

        int len = 0;

        //包头
        for (int i = 0; i < UdpCommand.PACK_FRAME_HEADER.length; i++) {
            srcBytes[i] = UdpCommand.PACK_FRAME_HEADER[i];
        }
        len += UdpCommand.PACK_FRAME_HEADER.length;

        //类型
        byte[] paramdata = parambytes;//TxData.getInstance().getParam();//参数
        if (paramdata != null && paramdata.length > 0) {
            for (int i = 0; i < paramdata.length; i++) {
                srcBytes[len + i] = paramdata[i];
            }
            len += paramdata.length;
        }

        //byte[] data = TxData.getInstance().getData();
        //长度
        byte[] lendata = DataTypeConversion.shortToBytes((short)databytes.length);
        for (int i = 0; i < lendata.length; i++) {
            srcBytes[len + i] = lendata[i];
        }
        len += lendata.length;

        //数据
        if (databytes != null && databytes.length > 0) {
            for (int i = 0; i < databytes.length; i++) {
                srcBytes[len + i] = databytes[i];
            }
            len += databytes.length;
        }

        //加验证
        int resultLen = UdpDataUtil.comPackage(srcBytes, resBytes, len);

        byte[] sendData = new byte[resultLen];
        System.arraycopy(resBytes, 0, sendData, 0, resultLen);

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    InetAddress ipAddress = InetAddress.getByName(serverIp);
                    sendPacket = new DatagramPacket(sendData, sendData.length, ipAddress, serverPort);
                    mSocket.send(sendPacket);
                    Log.d(TAG,"sendByteCmd DatagramPacket");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    private static final int MSG_CON_SUC = 1000000;
    private static final int MSG_CON_FAIL = 1000001;
    private static final int MSG_REV_DATA = 1000002;

    @SuppressLint("HandlerLeak")
    Handler uiHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch(msg.what) {
                //connect error
                case MSG_CON_FAIL:
                    /*if (null != onDataReceiveListener) {
                        onDataReceiveListener.onConnectFail();
                        disconnect();
                    }*/
                    break;
                //connect success
                case MSG_CON_SUC:
                    /*if (null != onDataReceiveListener) {
                        onDataReceiveListener.onConnectSuccess();
                    }*/
                    break;
                //receive data
                case MSG_REV_DATA:
                    Bundle bundle = msg.getData();
                    byte[] buffer = bundle.getByteArray("data");
                    int size = bundle.getInt("size");
//                    int mequestCode = bundle.getInt("requestCode");
                    if (null != onDataReceiveListener) {
                        onDataReceiveListener.onDataReceive(buffer, size/*, mequestCode*/);
                    }
                    break;
            }
        }
    };

    private OnDataReceiveListener onDataReceiveListener = null;
//    private int requestCode = -1;

    public interface OnDataReceiveListener {
        /*void onConnectSuccess();
        void onConnectFail();*/
        void onDataReceive(byte[] buffer, int size/*, int requestCode*/);
    }

    public void setOnDataReceiveListener(
            OnDataReceiveListener dataReceiveListener) {
        onDataReceiveListener = dataReceiveListener;
    }

    //https://www.jianshu.com/p/9dbb8ac146f6?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes
    //https://blog.51cto.com/u_16099274/6531400
}
